package com.vibes.actors

import akka.actor.{Actor, ActorRef, Props}
import akka.util.Timeout
import com.typesafe.scalalogging.LazyLogging
import com.vibes.actions.{DiscoveryActions, NodeActions}
import com.vibes.models.VNode
import com.vibes.utils.VConf

import scala.concurrent.duration._
import scala.util.Random

/**
  * DiscoveryActor根据配置参数neighbourDiscoveryInterval定期更新节点的邻居表。
  * DiscoveryActor只需每隔neighbourDiscoveryInterval秒向每个节点发送一条消息，并随机选择一组新的邻居。
* I believe there is no specific algorithm or heuristic behind the neighbour discovery in a blockchain based network, but even if there is - it
* is easy to exchange the current randomized one since the DiscoveryActor is the central authority there.
*我认为，在基于区块链的网络中，邻居发现背后没有特定的算法或启发式算法，但即使有-也很容易交换当前的随机算法，
*因为DiscoveryActor是那里的中央权威机构。
  *
  * 为什么节点不更新自己的表，而是由另一个参与者管理？因为想把工作从他们身上卸下来，其次，在这里实现非常容易。
**/
//DiscoveryActor作为Master的成员变量
class DiscoveryActor(numberOfNeighbours: Int) extends Actor with LazyLogging {
  private var currentNodes: List[VNode] = List.empty
  private var currentNodeMap: Map[ActorRef,String]=Map.empty
  implicit private val timeout: Timeout = Timeout(20.seconds)

  override def preStart(): Unit = {
    logger.debug(s"DiscoveryActor started ${self.path}")
  }

  override def receive: Receive = {
    //由NodeActor的preStart()调用，相当于注册节点的过程
    case DiscoveryActions.ReceiveNode(node) =>
      currentNodes = DiscoveryActor.updateCurrentNodes(currentNodes, node)//更新currentNodes
      currentNodeMap+=(node.actor -> node.id)


    case DiscoveryActions.AnnounceNeighbours =>
      DiscoveryActor.announceNeighbours(currentNodes, numberOfNeighbours)//更新邻居


    case DiscoveryActions.printBian=>
      DiscoveryActor.printBian(currentNodes, currentNodeMap)


  }
}

object DiscoveryActor extends LazyLogging {
  def props(numberOfNeighbours: Int): Props = Props(new DiscoveryActor(numberOfNeighbours)) //Props是akka组件的东西，使用Props创建一个Actor

  //更新邻居集
  def announceNeighbours(currentNodes: List[VNode], numberOfNeighbours: Int): Unit = {
    logger.debug("Update neighbours table")
    currentNodes.foreach(node =>
      //discoverNeighbours方法时生成一个备选项的set集合，再发送给node的actor去接收ReceiveNeighbours类型消息
      node.actor ! NodeActions.ReceiveNeighbours(discoverNeighbours(currentNodes, node, numberOfNeighbours))//currentNodes是all节点集
    )

  }
  def printBian(currentNodes: List[VNode],currentNodeMap:Map[ActorRef,String]):Unit={
    currentNodes.foreach(node =>{
      node.neighbourActors.foreach(neibor=>{
        logger.debug(s"bian from:${node.id} to:${currentNodeMap.get(neibor)}")
      })
    })
  }

  //这个函数干了啥：从currentNodes里面挑出不是node的，再随机挑numberOfNeighbours个，组成一个Set返回
  //
  def discoverNeighbours1(currentNodes: List[VNode], node: VNode, numberOfNeighbours: Int): Set[ActorRef] = {
      if (VConf.isAlternativeHistoryAttack && !VConf.attackSuccessful && !VConf.attackFailed && (VConf.evilChainLength > 0 || VConf.goodChainLength > 0)) {
        var nodes = currentNodes.filter(_.isMalicious == node.isMalicious) //Malicious恶意的
        nodes = nodes.filter(_ != node)
        Random.shuffle(nodes).take(numberOfNeighbours).map(_.actor).toSet
      } else {  //上面的if不用看 ，跟攻击相关
        val nodes = currentNodes.filter(_ != node)  //从currentNodes中筛选出不是 传入参数node的，形成nodes
        Random.shuffle(nodes).take(numberOfNeighbours).map(_.actor).toSet    //
      }
  }

  /**
    * 更新node的邻居集合
    * 返回的只是一个备选集合（numberOfNeighbours个节点），不是指定邻居集合
    * 真正更新邻居列表的动作是在VNode类中，将备选项与原有的邻居表混合再随机选numberOfNeighbours个
    * @param currentNodes 当前node列表
    * @param node 本node 因为要剔除掉自己，自己不能和自己做邻居
    * @param numberOfNeighbours 邻居数量
    * @return 邻居集合的备选项set
    */
  def discoverNeighbours(currentNodes: List[VNode], node: VNode, numberOfNeighbours: Int): Set[ActorRef] = {
    if (VConf.isAlternativeHistoryAttack && !VConf.attackSuccessful && !VConf.attackFailed && (VConf.evilChainLength > 0 || VConf.goodChainLength > 0)) {
      var nodes = currentNodes.filter(_.isMalicious == node.isMalicious) //Malicious恶意的
      nodes = nodes.filter(_ != node)
      Random.shuffle(nodes).take(numberOfNeighbours).map(_.actor).toSet
    }//上面的if不用看 ，跟攻击相关

    else {
      val nodes = currentNodes.filter(_ != node)  //从currentNodes中筛选出不是 传入参数node的，形成nodes
      if(node.isListener.contains(false)){  //普通节点
        Random.shuffle(nodes).take(numberOfNeighbours).map(_.actor).toSet
        //shuffle:随机重新排序
        //take(n):选择前n个
      }else{   //监听节点
        Random.shuffle(nodes).take(VConf.numberOfNodes*VConf.rateOfListenerNeighbours/100).map(_.actor).toSet
      }
    }
  }


  /**
    * DiscoverActor也作为一个全局的Actor，这里更新他知道的节点node集
    * 作用1：初始化阶段把创建的Node添加给DiscoverActor的node列表中
    * 作用2：更新node列表的状态，如node的本地链长度
    * @param currentNodes 当前node列表
    * @param node 新ndoe
    * @return 新node列表
    */
  def updateCurrentNodes(currentNodes: List[VNode], node: VNode): List[VNode] = {
    //如果currentNodes里面不存在传进来的node.id....意思是新认识了一个新节点
    if (!currentNodes.exists(_.id == node.id)) {
      return node :: currentNodes // x::list  向list中添加元素node
    }
    //如果传入的node已经在current里面有了，就检查下传入的和已有的，俩谁的链更长
    //相当于更新node的状态
    currentNodes.map(
      currentNode =>
        if (currentNode.id == node.id && node.blockchain.size > currentNode.blockchain.size) node
        else currentNode)
  }
}
